package com.xinsoft.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinsoft.common.exception.BDException;
import com.xinsoft.constant.Const;
import com.xinsoft.entity.dto.*;
import com.xinsoft.entity.po.ScmConsumingApply;
import com.xinsoft.entity.po.ScmConsumingApplyItem;
import com.xinsoft.entity.po.ScmPushBill;
import com.xinsoft.entity.vo.*;
import com.xinsoft.mapper.ScmConsumingApplyItemMapper;
import com.xinsoft.service.*;
import com.xinsoft.mapper.ScmConsumingApplyMapper;
import com.xinsoft.utils.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ZYM
 * @description 针对表【scm_consuming_apply(领用申请)】的数据库操作Service实现
 * @createDate 2023-09-08 13:06:55
 */
@Slf4j
@Service
public class ScmConsumingApplyServiceImpl extends ServiceImpl<ScmConsumingApplyMapper, ScmConsumingApply>
        implements ScmConsumingApplyService {

    @Resource
    private ScmConsumingApplyItemMapper consumingApplyItemMapper;

    @Autowired
    private SysDocumentNumService sysDocumentNumService;
    @Autowired
    private ScmConsumingApplyItemService consumingApplyItemService;
    @Autowired
    private SysMaterialService materialService;
    @Autowired
    private SyncPullBillAdapter syncPullBillAdapter;
    @Autowired
    private ScmPushBillService pushBillService;

    @Override
    public IPage<ScmConsumingApplyPageVo> findPageListByParam(ScmConsumingApplyParam param) {
        Page<ScmConsumingApplyPageVo> page = new Page<>(param.getCurrentPage(), param.getPageSize());
        IPage<ScmConsumingApplyPageVo> pageList = baseMapper.findListByParam(page, param);
        return pageList;
    }

    @Override
    public List<ScmConsumingApplyPageVo> findListByParam(ScmConsumingApplyParam param) {
        return baseMapper.findListByParam(param);
    }

    @Override
    public Integer tempSave(ScmConsumingApplyDTO dto) {
        ScmConsumingApply apply = new ScmConsumingApply();
        BeanUtils.copyProperties(dto, apply);
        apply.setAuditStatus(Const.AUDIT_STATUS_WAIT);
        List<ScmConsumingApplyItem> itemList = dto.getScmConsumingApplyItemList();
        this.checkNum(dto.getApplyTypeId(), itemList);
        // 保存或新增
        if (Objects.isNull(apply.getId())) {
            // 更新流水号
            sysDocumentNumService.incrementLastSerialByDocumentId(apply.getApplyTypeId());
        } else {
            consumingApplyItemService.remove(Wrappers.lambdaQuery(ScmConsumingApplyItem.class).eq(ScmConsumingApplyItem::getApplyId, apply.getId()));
        }

        this.saveOrUpdate(apply);
        // 保存领用申请明细
        itemList.forEach(item -> {
            item.setApplyId(apply.getId());
            item.setOutNum(BigDecimal.ZERO);
        });
        consumingApplyItemService.saveBatch(itemList);

        return apply.getId();
    }

    /**
     * 校验数量
     *
     * @param applyTypeId 申请单类型
     * @param itemList    生产领用明细
     */
    public void checkNum(Integer applyTypeId, List<ScmConsumingApplyItem> itemList) {
        List<String> failMsgList = new ArrayList<>();
        List<Integer> sourceIds = itemList.stream().map(ScmConsumingApplyItem::getSourceId).distinct().collect(Collectors.toList());

        // 校验可执行数量
        // 关联金蝶生产投料单
        switch (applyTypeId) {
            case 10786:     // 生产领用申请
                Object data = syncPullBillAdapter.feedingDetailSync(sourceIds).getData();
                List<ErpProductionFeedingDetailVO> feedingDetailVOList = JSON.parseArray(JSON.toJSONString(data), ErpProductionFeedingDetailVO.class);
                Map<String, ErpProductionFeedingDetailVO> voMap = feedingDetailVOList.stream().collect(Collectors.toMap(vo -> String.join("_", vo.getHeadId().toString(), vo.getMaterialId().toString()), vo -> vo));
                itemList.forEach(item -> {
                    String key = String.join("_", item.getSourceId().toString(), item.getMaterialId().toString());
                    if (voMap.containsKey(key)) {
                        ErpProductionFeedingDetailVO vo = voMap.get(key);
                        BigDecimal availableCount = vo.getPlanFeeding().subtract(vo.getOutCount());
                        String availableCountStr = availableCount.stripTrailingZeros().toPlainString();
                        String planFeedingCountStr = vo.getPlanFeeding().stripTrailingZeros().toPlainString();
                        String outCountStr = vo.getOutCount().stripTrailingZeros().toPlainString();
                        log.info("物料：{}，计划投料数量：{}，已出数量：{}，可用数量：{}", vo.getMaterialName(), planFeedingCountStr, outCountStr, availableCountStr);
                        if (item.getConsumingNum().compareTo(availableCount) > 0) {
                            failMsgList.add(String.format("物料[%s]的计划投料数量：%s，已出数量：%s，可用数量：%s；", vo.getMaterialName(), planFeedingCountStr, outCountStr, availableCountStr));
                        }
                    }
                });
                break;
            case 10788:     // 委外领用申请
                Object data1 = syncPullBillAdapter.sourcingItem(sourceIds).getData();
                List<ErpOutSourcingItemVO> sourcingFeedingItemVOList = JSON.parseArray(JSON.toJSONString(data1), ErpOutSourcingItemVO.class);
                Map<String, List<ErpOutSourcingItemVO>> sourcingFeedingItemVOGroup = sourcingFeedingItemVOList.stream().collect(Collectors.groupingBy(vo -> String.join("_", vo.getHeadId().toString(), vo.getMaterialId().toString())));
                itemList.forEach(item -> {
                    String key = String.join("_", item.getSourceId().toString(), item.getMaterialId().toString());
                    if (sourcingFeedingItemVOGroup.containsKey(key)) {
                        List<ErpOutSourcingItemVO> vo = sourcingFeedingItemVOGroup.get(key);
                        BigDecimal fQty = vo.stream().map(ErpOutSourcingItemVO::getFQty).filter(item2 -> ObjectUtil.isNotEmpty(item2)).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                        BigDecimal auxFtockQty = vo.stream().map(ErpOutSourcingItemVO::getAuxFtockQty).filter(item2 -> ObjectUtil.isNotEmpty(item2)).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                        BigDecimal availableCount = fQty.subtract(auxFtockQty);
                        String availableCountStr = availableCount.stripTrailingZeros().toPlainString();
                        String numStr = fQty.stripTrailingZeros().toPlainString();
                        String receiveNumStr = auxFtockQty.stripTrailingZeros().toPlainString();
                        log.info("物料：{}，计划投料数量：{}，已出数量：{}，可用数量：{}", vo.get(0).getMaterialName(), numStr, receiveNumStr, availableCountStr);
                        if (item.getConsumingNum().compareTo(availableCount) > 0) {
                            failMsgList.add(String.format("物料[%s]的计划投料数量：%s，已出数量：%s，可用数量：%s；", vo.get(0).getMaterialName(), numStr, receiveNumStr, availableCountStr));
                        }
                    }
                });
                break;
            case 10789:     // 其他领用申请

                break;
        }

        //todo 暂时先不做校验，因为金蝶这块的数量字段有很多的bug，校验容易让接口不通
//        if (CollectionUtil.isNotEmpty(failMsgList)) {
//            failMsgList.add("领用数量不能大于源单可用数量");
//            throw new BDException(String.join(",\n", failMsgList));
//        }
    }

    @Override
    public ScmConsumingApplyVo detail(Integer id) {
        ScmConsumingApplyVo vo = baseMapper.getById(id);
        List<ScmConsumingApplyItemVo> itemVoList = consumingApplyItemMapper.listByApplyId(id);

        Map<Integer, ScmPushBill> waitPushBillMap = new HashMap<>();
        if (Objects.equals(vo.getApplyTypeId(),Const.DOCUMENT_ID.PRODUCE_CONSUMING_APPLY_DOCUMENT)){
            waitPushBillMap = pushBillService.getWaitPushBillMap(Const.BillType.SCLYCK, id);
        }
        if (Objects.equals(vo.getApplyTypeId(),Const.DOCUMENT_ID.OUTSOURCING_CONSUMING_APPLY_DOCUMENT)){
            waitPushBillMap = pushBillService.getWaitPushBillMap(Const.BillType.WWLYCK, id);
        }
        ErpRealTimePageParam param = new ErpRealTimePageParam();
        param.setCurrentPage(1);
        param.setPageSize(100);
        Map<Integer, ScmPushBill> finalWaitPushBillMap = waitPushBillMap;
        itemVoList.forEach(itemVo -> {
            if (Objects.nonNull(finalWaitPushBillMap)){
                itemVo.setExistNotSubmit(finalWaitPushBillMap.containsKey(itemVo.getMaterialId()));
            }
            param.setMaterialId(itemVo.getMaterialId().toString());
            List<ErpRealTimeDto> records = syncPullBillAdapter.realtimeInventory(param).getRecords();
            List<ErpRealTimeDto> realTimeDtoList = records.stream()
                    // .filter(dto -> !Objects.equals(dto.getWarehouseItemId(), 0) || !Objects.equals(dto.getWarehouseItemName(), "*"))
                    .limit(3)
                    .collect(Collectors.toList());
            List<Map<String, Object>> list = new ArrayList<>();
            realTimeDtoList.forEach(realTimeDto -> {
                list.add(BeanUtil.beanToMap(realTimeDto));
            });
            itemVo.setRealtimeInventoryList(list);
        });
        vo.setScmConsumingApplyItemList(itemVoList);
        return vo;
    }


    @Override
    public void del(List<Integer> ids) {
        List<ScmConsumingApply> list = this.list(Wrappers.lambdaQuery(ScmConsumingApply.class)
                .eq(ScmConsumingApply::getAuditStatus, Const.AUDIT_STATUS_NORMAL)
                .in(ScmConsumingApply::getId, ids));
        if (list.size() > 0) {
            throw new BDException("操作失败，已审核的单据不能删除");
        }
        consumingApplyItemService.remove(Wrappers.lambdaQuery(ScmConsumingApplyItem.class).in(ScmConsumingApplyItem::getApplyId, ids));
        this.removeByIds(ids);
    }

    @Override
    public void audit(Integer id) {
        ScmConsumingApply apply = this.getById(id);
        if (Const.AUDIT_STATUS_NORMAL.equals(apply.getAuditStatus())) {
            throw new BDException("该单据已审核，无需重复审核");
        }
        List<ScmConsumingApplyItem> itemList = consumingApplyItemService.list(Wrappers.lambdaQuery(ScmConsumingApplyItem.class)
                .eq(ScmConsumingApplyItem::getApplyId, id));
        this.checkNum(apply.getApplyTypeId(), itemList);
        // 1.审核
        apply.setAuditId(TokenUtil.getUserId());
        apply.setAuditTime(new Date());
        apply.setAuditStatus(Const.AUDIT_STATUS_NORMAL);
        this.updateById(apply);
    }

    @Override
    public void reverseAudit(Integer id) {
        ScmConsumingApply apply = this.getById(id);
        if (Const.AUDIT_STATUS_WAIT.equals(apply.getAuditStatus())) {
            throw new BDException("该单据未审核，无需反审核");
        }
        List<ScmConsumingApplyItem> itemList = consumingApplyItemService.list(Wrappers.lambdaQuery(ScmConsumingApplyItem.class)
                .eq(ScmConsumingApplyItem::getApplyId, id));
        List<ScmConsumingApplyItem> items = itemList.stream()
                .filter(item -> Objects.nonNull(item.getOutNum()) && item.getOutNum().compareTo(BigDecimal.ZERO) > 0)
                .collect(Collectors.toList());
        if (items.size() > 0) {
            throw new BDException("该单据已产生下游单据，不能反审核");
        }

        // 1.反审核
        apply.setAuditId(null);
        apply.setAuditTime(null);
        apply.setAuditStatus(Const.AUDIT_STATUS_WAIT);
        this.updateById(apply);
    }
}




